
#define BATTLE_PET_STAMINA_LIMIT_HOUR	4

#include "../../engine.h"

#include "../../User.h"
#include "../../Session.h"

#include "../../Proxy.h"

#include "../../NLogic/Static.h"
#include "../../NLogic/Battle.h"
#include "../../Enum.h"
#include "../../constant.h"
#include "world.h"

namespace ProjectZ { namespace NCommand { namespace NWorld {

void moveOldWorld(User * pUser)
{
	NLogic::Space * pSpace = NULL;
	NLogic::Space * pWorld = pUser->_getWorldSpace();
	if (NULL == pWorld)
	{
		auto worldTree = NLogic::Static::Instance().GETWORLDSPACETREE();
		auto iter = worldTree.begin();

		// test code
		for(; iter != worldTree.end(); ++iter)
		{
			if(Config::InstancePtr()->GetResourceDatabase()._maxChannelUser <= iter->second->GetCCU())
				continue;

			pSpace = iter->second;
			break;
		}

		if (NULL != pSpace)
			pUser->_setWorldSpace(pSpace);
	}
	else if(Config::InstancePtr()->GetResourceDatabase()._maxChannelUser <= pWorld->GetCCU())
	{
		auto worldTree = NLogic::Static::Instance().GETWORLDSPACETREE();
		auto iter = worldTree.begin();
		for(; iter != worldTree.end(); ++iter)
		{
			if(Config::InstancePtr()->GetResourceDatabase()._maxChannelUser <= iter->second->GetCCU())
				continue;

			pSpace = iter->second;
			break;
		}
		if (NULL != pSpace)
			pUser->_setWorldSpace(pSpace);
	}
	else
	{
		pSpace = pWorld;
	}

	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();

	if(NULL == pSpace)
	{
		* pAck << static_cast<uint16_t>(ACK_CHANNEL_USER_FULL);
		* pAck << pUser->GetUserSeq();
		* pAck << static_cast<uint16_t>(0);
		* pAck << static_cast<uint16_t>(0);
		* pAck << static_cast<uint16_t>(0);
		pAck->MakePacket(CMD_SC_INPUTINFO);
		pUser->Send(pAck);
		return;
	}
	else
	{
		// WORLD 진입 시 유저의 EnumState 초기화
		pUser->SetEnumState(User::EnumState::__NONE__);
		pSpace->EnterUser(pUser);

		* pAck << static_cast<uint16_t>(ACK_OK);
		* pAck << pUser->GetUserSeq();
		* pAck << static_cast<uint16_t>(pUser->GetBattle()->GetData().x);
		* pAck << static_cast<uint16_t>(pUser->GetBattle()->GetData().y);
		* pAck << static_cast<uint16_t>(pSpace->GetData()._index);
		pAck->MakePacket(CMD_SC_INPUTINFO);
		pUser->Send(pAck);
	}
}

void
RecallProcess(User * pUser, uint32_t & recallUserSeq)
{
	NLogic::Space * pSpace = pUser->GetSpace();
	if (NULL != pSpace)
		pSpace->LeaveUser(pUser);

	NLogic::Space * pReserveSpace = pUser->GetReserveSpace();
	if(NULL == pReserveSpace)
	{
		moveOldWorld(pUser);
		return;
	}


	User * pRecallUser = ::ProjectZ::NProxy::GetUser(recallUserSeq);
	if(NULL == pRecallUser)
	{
		pReserveSpace->EraseReserveUser(pUser);
		pUser->SetReserveSpace(NULL);
		moveOldWorld(pUser);
		return;
	}

	NLogic::Space * pRecallSpace = pRecallUser->GetSpace();
	if(NULL == pRecallSpace)
	{
		pReserveSpace->EraseReserveUser(pUser);
		pUser->SetReserveSpace(NULL);
		moveOldWorld(pUser);
		return;
	}

	if(NLogic::Space::SpaceType::WORLD != pRecallSpace->GetData()._spaceType)
	{
		pReserveSpace->EraseReserveUser(pUser);
		pUser->SetReserveSpace(NULL);
		moveOldWorld(pUser);
		return;
	}

	if(false == pReserveSpace->FindReserveUser(pUser->GetUserSeq()))
	{
		pReserveSpace->EraseReserveUser(pUser);
		pUser->SetReserveSpace(NULL);
		moveOldWorld(pUser);
		return;
	}

	pSpace = pRecallSpace;
//	pSpace = pReserveSpace;
	pReserveSpace->EraseReserveUser(pUser);
	pUser->SetReserveSpace(NULL);

	pUser->_setWorldSpace(pSpace);

	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
	* pAck << static_cast<uint16_t>(ACK_OK);
	* pAck << pUser->GetUserSeq();
	* pAck << static_cast<uint16_t>(pRecallUser->GetBattle()->GetData().x);
	* pAck << static_cast<uint16_t>(pRecallUser->GetBattle()->GetData().y);

	* pAck << static_cast<uint16_t>(pSpace->GetData()._index);
	pAck->MakePacket(CMD_SC_INPUTINFO);
	pUser->Send(pAck);
	// WORLD 진입 시 유저의 EnumState 초기화
	pUser->SetEnumState(User::EnumState::__NONE__);
	pSpace->EnterUser(pUser, pRecallUser->GetBattle()->GetData().x, pRecallUser->GetBattle()->GetData().y);
}

void
ChannelChangeProcess(User * pUser, uint16_t & channel_id)
{
	NLogic::Space * pSpace = pUser->GetSpace();
	if (NULL != pSpace)
		pSpace->LeaveUser(pUser);

	auto worldTree = NLogic::Static::Instance().GETWORLDSPACETREE();
	auto iter = worldTree.find(channel_id);
	if(worldTree.end() == iter)
		return;

	if(Config::InstancePtr()->GetResourceDatabase()._maxChannelUser <= iter->second->GetCCU())
	{
		NLogic::Space * pReserveSpace = pUser->GetReserveSpace();
		if( NULL == pReserveSpace)
		{
			moveOldWorld(pUser);
			return;
		}

		if(false == iter->second->FindReserveUser(pUser->GetUserSeq()))
		{
			pReserveSpace->EraseReserveUser(pUser);
			pUser->SetReserveSpace(NULL);
			moveOldWorld(pUser);
			return;
		}

		if(false == iter->second->EraseReserveUser(pUser))
		{
			moveOldWorld(pUser);
			return;
		}

		pUser->SetReserveSpace(NULL);
	}


	pSpace = iter->second;

	pUser->_setWorldSpace(pSpace);
	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();
	* pAck << static_cast<uint16_t>(ACK_OK);
	* pAck << pUser->GetUserSeq();
	* pAck << static_cast<uint16_t>(ConstPosX);
	* pAck << static_cast<uint16_t>(ConstPosY);

	* pAck << static_cast<uint16_t>(pSpace->GetData()._index);
	pAck->MakePacket(CMD_SC_INPUTINFO);
	pUser->Send(pAck);

	NLogic::Space * pReserveSpace = pUser->GetReserveSpace();
	if(NULL != pReserveSpace)
	{
		pReserveSpace->EraseReserveUser(pUser);
		pUser->SetReserveSpace(NULL);
	}

	// WORLD 진입 시 유저의 EnumState 초기화
	pUser->SetEnumState(User::EnumState::__NONE__);
	pSpace->EnterUser(pUser);
}



void
CS_INPUTINFO::OnExecute(User * pUser, ::Network::Packet * pPacket)
{
	/*
	 * 유저에 외형 정보 저장 요청 패킷
	CS_REQ_ENTER_MY_INFO	0x0301		1	u8	접속슬롯정보

	유저에 외형 정보 저장 응답 패킷
	SC_REQ_ENTER_MY_INFO	0x0302		2	signed short	성공 / 실패 ( 1/ -1)
				4	unsigned short	GID : 게임 유니크 Key
				2	unsigned short	x : x 좌표
				2	unsigned short	y : y 좌표
					string	슬롯 아이디
	 *
	 *
	 */
	STRESSLOG("[CHANNEL] CS_INPUTINFO::ONEXECUTE");
	PREPARE_PACKET;
	if( pUser->GetLogin() == false )
	{
		DEBUGLOG("WARNING: CS_INPUTINFO GID : %d, not login", pUser->GetUserSeq());
		return;
	}

	uint32_t recallUserSeq = 0;
	uint16_t channel_id = 0;
	*pPacket >> recallUserSeq >> channel_id;

	DEBUGLOG("%s userseq = %d , recallUserSeq = %d, Channel_ID = %d", __FUNCTION__, pUser->GetUserSeq(), recallUserSeq, channel_id);

	if(0 < recallUserSeq && 0 < channel_id)
	{
		RecallProcess(pUser, recallUserSeq);
		return;
	}

	if(0 < channel_id)
	{
		ChannelChangeProcess(pUser, channel_id);
		return;
	}

	NLogic::Space * pSpace = pUser->GetSpace();
	if (pSpace != NULL)
		pSpace->LeaveUser(pUser);

	NLogic::Space * pWorld = pUser->_getWorldSpace();
	if (NULL == pWorld)
	{
		DEBUGLOG("CS_INPUTINFO OTHER INPUT NICK : %s", pUser->GetUserInfo().nickname.c_str());
		auto worldTree = NLogic::Static::Instance().GETWORLDSPACETREE();
		auto iter = worldTree.begin();
		// test code
		for(; iter != worldTree.end(); ++iter)
//			for(int i = 0; i < 2; ++iter, i++)
		{
			if(Config::InstancePtr()->GetResourceDatabase()._maxChannelUser <= iter->second->GetCCU())
				continue;

			pSpace = iter->second;
			break;
		}
		if (NULL != pSpace)
			pUser->_setWorldSpace(pSpace);
	}
	else if(Config::InstancePtr()->GetResourceDatabase()._maxChannelUser <= pWorld->GetCCU())
	{
		auto worldTree = NLogic::Static::Instance().GETWORLDSPACETREE();
		auto iter = worldTree.begin();
		for(; iter != worldTree.end(); ++iter)
		{
			if(Config::InstancePtr()->GetResourceDatabase()._maxChannelUser <= iter->second->GetCCU())
				continue;

			pSpace = iter->second;
			break;
		}

		if (NULL != pSpace)
			pUser->_setWorldSpace(pSpace);
	}
	else
	{
		pSpace = pWorld;
	}

	::Network::Packet * pAck = ::Network::GETPACKETPOOL()->SEND_NEW();

	if(NULL == pSpace)
	{
		* pAck << static_cast<uint16_t>(ACK_CHANNEL_USER_FULL);
		* pAck << pUser->GetUserSeq();
		* pAck << static_cast<uint16_t>(0);
		* pAck << static_cast<uint16_t>(0);
		* pAck << static_cast<uint16_t>(0);
		pAck->MakePacket(CMD_SC_INPUTINFO);
		pUser->Send(pAck);
		return;
	}
	else
	{
		// WORLD 진입 시 유저의 EnumState 초기화
		pUser->SetEnumState(User::EnumState::__NONE__);
		pSpace->EnterUser(pUser);

		* pAck << static_cast<uint16_t>(ACK_OK);
		* pAck << pUser->GetUserSeq();
		* pAck << static_cast<uint16_t>(pUser->GetBattle()->GetData().x);
		* pAck << static_cast<uint16_t>(pUser->GetBattle()->GetData().y);
		* pAck << static_cast<uint16_t>(pSpace->GetData()._index);
		pAck->MakePacket(CMD_SC_INPUTINFO);
		pUser->Send(pAck);
	}

//	ServerLocation svrLoc;
//	svrLoc.server_id = ::ProjectZ::Config::Instance().GetResourceDatabase()._channelUniqueIndex;
//	svrLoc.channel_id = pSpace->GetData()._index;
//
//	::ProjectZ::NLogic::Memcached * clsMemcached = ::ProjectZ::NLogic::Static::Instance().GETMEMCACHED();
//	clsMemcached->Set(std::to_string(pUser->GetUserSeq()).c_str(), reinterpret_cast<char*>(&svrLoc), sizeof(ServerLocation), 0);
}

} /*NWorld*/ } /*NCommand*/ } /*ProjectZ*/

